* Copyright (c) 2015,2016 Citrix Systems Ltd.
*/
+#include <xen/compiler.h>
#include <xen/hypercall.h>
+#include <xen/trace.h>
+
+extern hypercall_fn_t *const hypercall_table[NR_hypercalls],
+ *const compat_hypercall_table[NR_hypercalls];
#define ARGS(x, n) \
[ __HYPERVISOR_ ## x ] = (n)
#undef ARGS
+void pv_hypercall(struct cpu_user_regs *regs)
+{
+ struct vcpu *curr = current;
+#ifndef NDEBUG
+ unsigned long old_rip = regs->rip;
+#endif
+ unsigned long eax;
+
+ ASSERT(guest_kernel_mode(curr, regs));
+
+ eax = is_pv_32bit_vcpu(curr) ? regs->_eax : regs->eax;
+
+ if ( (eax >= NR_hypercalls) || !hypercall_table[eax] )
+ {
+ regs->eax = -ENOSYS;
+ return;
+ }
+
+ if ( !is_pv_32bit_vcpu(curr) )
+ {
+ unsigned long rdi = regs->rdi;
+ unsigned long rsi = regs->rsi;
+ unsigned long rdx = regs->rdx;
+ unsigned long r10 = regs->r10;
+ unsigned long r8 = regs->r8;
+ unsigned long r9 = regs->r9;
+
+#ifndef NDEBUG
+ /* Deliberately corrupt parameter regs not used by this hypercall. */
+ switch ( hypercall_args_table[eax] )
+ {
+ case 0: rdi = 0xdeadbeefdeadf00dUL;
+ case 1: rsi = 0xdeadbeefdeadf00dUL;
+ case 2: rdx = 0xdeadbeefdeadf00dUL;
+ case 3: r10 = 0xdeadbeefdeadf00dUL;
+ case 4: r8 = 0xdeadbeefdeadf00dUL;
+ case 5: r9 = 0xdeadbeefdeadf00dUL;
+ }
+#endif
+ if ( unlikely(tb_init_done) )
+ {
+ unsigned long args[6] = { rdi, rsi, rdx, r10, r8, r9 };
+
+ __trace_hypercall(TRC_PV_HYPERCALL_V2, eax, args);
+ }
+
+ regs->eax = hypercall_table[eax](rdi, rsi, rdx, r10, r8, r9);
+
+#ifndef NDEBUG
+ if ( regs->rip == old_rip )
+ {
+ /* Deliberately corrupt parameter regs used by this hypercall. */
+ switch ( hypercall_args_table[eax] )
+ {
+ case 6: regs->r9 = 0xdeadbeefdeadf00dUL;
+ case 5: regs->r8 = 0xdeadbeefdeadf00dUL;
+ case 4: regs->r10 = 0xdeadbeefdeadf00dUL;
+ case 3: regs->rdx = 0xdeadbeefdeadf00dUL;
+ case 2: regs->rsi = 0xdeadbeefdeadf00dUL;
+ case 1: regs->rdi = 0xdeadbeefdeadf00dUL;
+ }
+ }
+#endif
+ }
+ else
+ {
+ unsigned int ebx = regs->_ebx;
+ unsigned int ecx = regs->_ecx;
+ unsigned int edx = regs->_edx;
+ unsigned int esi = regs->_esi;
+ unsigned int edi = regs->_edi;
+ unsigned int ebp = regs->_ebp;
+
+#ifndef NDEBUG
+ /* Deliberately corrupt parameter regs not used by this hypercall. */
+ switch ( compat_hypercall_args_table[eax] )
+ {
+ case 0: ebx = 0xdeadf00d;
+ case 1: ecx = 0xdeadf00d;
+ case 2: edx = 0xdeadf00d;
+ case 3: esi = 0xdeadf00d;
+ case 4: edi = 0xdeadf00d;
+ case 5: ebp = 0xdeadf00d;
+ }
+#endif
+
+ if ( unlikely(tb_init_done) )
+ {
+ unsigned long args[6] = { ebx, ecx, edx, esi, edi, ebp };
+
+ __trace_hypercall(TRC_PV_HYPERCALL_V2, eax, args);
+ }
+
+ regs->_eax = compat_hypercall_table[eax](ebx, ecx, edx, esi, edi, ebp);
+
+#ifndef NDEBUG
+ if ( regs->rip == old_rip )
+ {
+ /* Deliberately corrupt parameter regs used by this hypercall. */
+ switch ( compat_hypercall_args_table[eax] )
+ {
+ case 6: regs->_ebp = 0xdeadf00d;
+ case 5: regs->_edi = 0xdeadf00d;
+ case 4: regs->_esi = 0xdeadf00d;
+ case 3: regs->_edx = 0xdeadf00d;
+ case 2: regs->_ecx = 0xdeadf00d;
+ case 1: regs->_ebx = 0xdeadf00d;
+ }
+ }
+#endif
+ }
+
+ perfc_incr(hypercalls);
+}
+
/*
* Local variables:
* mode: C
#include <xen/sched.h>
#include <xen/trace.h>
-void __trace_hypercall_entry(void)
-{
- struct cpu_user_regs *regs = guest_cpu_user_regs();
- unsigned long args[6];
-
- if ( is_pv_32bit_vcpu(current) )
- {
- args[0] = regs->ebx;
- args[1] = regs->ecx;
- args[2] = regs->edx;
- args[3] = regs->esi;
- args[4] = regs->edi;
- args[5] = regs->ebp;
- }
- else
- {
- args[0] = regs->rdi;
- args[1] = regs->rsi;
- args[2] = regs->rdx;
- args[3] = regs->r10;
- args[4] = regs->r8;
- args[5] = regs->r9;
- }
-
- __trace_hypercall(TRC_PV_HYPERCALL_V2, regs->eax, args);
-}
-
void __trace_pv_trap(int trapnr, unsigned long eip,
int use_error_code, unsigned error_code)
{
BLANK();
#ifdef CONFIG_PERF_COUNTERS
- DEFINE(ASM_PERFC_hypercalls, PERFC_hypercalls);
DEFINE(ASM_PERFC_exceptions, PERFC_exceptions);
BLANK();
#endif
LOAD_C_CLOBBERED compat=1 ax=0
UNLIKELY_END(msi_check)
- movl UREGS_rax(%rsp),%eax
GET_CURRENT(bx)
- cmpl $NR_hypercalls,%eax
- jae compat_bad_hypercall
-#ifndef NDEBUG
- /* Deliberately corrupt parameter regs not used by this hypercall. */
- pushq UREGS_rbx(%rsp); pushq %rcx; pushq %rdx; pushq %rsi; pushq %rdi
- pushq UREGS_rbp+5*8(%rsp)
- leaq compat_hypercall_args_table(%rip),%r10
- movl $6,%ecx
- subb (%r10,%rax,1),%cl
- movq %rsp,%rdi
- movl $0xDEADBEEF,%eax
- rep stosq
- popq %r8 ; popq %r9 ; xchgl %r8d,%r9d /* Args 5&6: zero extend */
- popq %rdx; popq %rcx; xchgl %edx,%ecx /* Args 3&4: zero extend */
- popq %rdi; popq %rsi; xchgl %edi,%esi /* Args 1&2: zero extend */
- movl UREGS_rax(%rsp),%eax
- pushq %rax
- pushq UREGS_rip+8(%rsp)
-#define SHADOW_BYTES 16 /* Shadow EIP + shadow hypercall # */
-#else
- /* Relocate argument registers and zero-extend to 64 bits. */
- xchgl %ecx,%esi /* Arg 2, Arg 4 */
- movl %edx,%edx /* Arg 3 */
- movl %edi,%r8d /* Arg 5 */
- movl %ebp,%r9d /* Arg 6 */
- movl UREGS_rbx(%rsp),%edi /* Arg 1 */
-#define SHADOW_BYTES 0 /* No on-stack shadow state */
-#endif
- cmpb $0,tb_init_done(%rip)
-UNLIKELY_START(ne, compat_trace)
- call __trace_hypercall_entry
- /* Restore the registers that __trace_hypercall_entry clobbered. */
- movl UREGS_rax+SHADOW_BYTES(%rsp),%eax /* Hypercall # */
- movl UREGS_rbx+SHADOW_BYTES(%rsp),%edi /* Arg 1 */
- movl UREGS_rcx+SHADOW_BYTES(%rsp),%esi /* Arg 2 */
- movl UREGS_rdx+SHADOW_BYTES(%rsp),%edx /* Arg 3 */
- movl UREGS_rsi+SHADOW_BYTES(%rsp),%ecx /* Arg 4 */
- movl UREGS_rdi+SHADOW_BYTES(%rsp),%r8d /* Arg 5 */
- movl UREGS_rbp+SHADOW_BYTES(%rsp),%r9d /* Arg 6 */
-#undef SHADOW_BYTES
-UNLIKELY_END(compat_trace)
- leaq compat_hypercall_table(%rip),%r10
- PERFC_INCR(hypercalls, %rax, %rbx)
- callq *(%r10,%rax,8)
-#ifndef NDEBUG
- /* Deliberately corrupt parameter regs used by this hypercall. */
- popq %r10 # Shadow RIP
- cmpq %r10,UREGS_rip+8(%rsp)
- popq %rcx # Shadow hypercall index
- jne compat_skip_clobber /* If RIP has changed then don't clobber. */
- leaq compat_hypercall_args_table(%rip),%r10
- movb (%r10,%rcx,1),%cl
- movl $0xDEADBEEF,%r10d
- testb %cl,%cl; jz compat_skip_clobber; movl %r10d,UREGS_rbx(%rsp)
- cmpb $2, %cl; jb compat_skip_clobber; movl %r10d,UREGS_rcx(%rsp)
- cmpb $3, %cl; jb compat_skip_clobber; movl %r10d,UREGS_rdx(%rsp)
- cmpb $4, %cl; jb compat_skip_clobber; movl %r10d,UREGS_rsi(%rsp)
- cmpb $5, %cl; jb compat_skip_clobber; movl %r10d,UREGS_rdi(%rsp)
- cmpb $6, %cl; jb compat_skip_clobber; movl %r10d,UREGS_rbp(%rsp)
-compat_skip_clobber:
-#endif
- movl %eax,UREGS_rax(%rsp) # save the return value
+ mov %rsp, %rdi
+ call pv_hypercall
/* %rbx: struct vcpu */
ENTRY(compat_test_all_events)
call compat_create_bounce_frame
jmp compat_test_all_events
-compat_bad_hypercall:
- movl $-ENOSYS,UREGS_rax(%rsp)
- jmp compat_test_all_events
-
/* %rbx: struct vcpu, interrupts disabled */
ENTRY(compat_restore_all_guest)
ASSERT_INTERRUPTS_DISABLED
testb $TF_kernel_mode,VCPU_thread_flags(%rbx)
jz switch_to_kernel
-/*hypercall:*/
- movq %r10,%rcx
- cmpq $NR_hypercalls,%rax
- jae bad_hypercall
-#ifndef NDEBUG
- /* Deliberately corrupt parameter regs not used by this hypercall. */
- pushq %rdi; pushq %rsi; pushq %rdx; pushq %rcx; pushq %r8 ; pushq %r9
- leaq hypercall_args_table(%rip),%r10
- movq $6,%rcx
- sub (%r10,%rax,1),%cl
- movq %rsp,%rdi
- movl $0xDEADBEEF,%eax
- rep stosq
- popq %r9 ; popq %r8 ; popq %rcx; popq %rdx; popq %rsi; popq %rdi
- movq UREGS_rax(%rsp),%rax
- pushq %rax
- pushq UREGS_rip+8(%rsp)
-#define SHADOW_BYTES 16 /* Shadow EIP + shadow hypercall # */
-#else
-#define SHADOW_BYTES 0 /* No on-stack shadow state */
-#endif
- cmpb $0,tb_init_done(%rip)
-UNLIKELY_START(ne, trace)
- call __trace_hypercall_entry
- /* Restore the registers that __trace_hypercall_entry clobbered. */
- movq UREGS_rax+SHADOW_BYTES(%rsp),%rax /* Hypercall # */
- movq UREGS_rdi+SHADOW_BYTES(%rsp),%rdi /* Arg 1 */
- movq UREGS_rsi+SHADOW_BYTES(%rsp),%rsi /* Arg 2 */
- movq UREGS_rdx+SHADOW_BYTES(%rsp),%rdx /* Arg 3 */
- movq UREGS_r10+SHADOW_BYTES(%rsp),%rcx /* Arg 4 */
- movq UREGS_r8 +SHADOW_BYTES(%rsp),%r8 /* Arg 5 */
- movq UREGS_r9 +SHADOW_BYTES(%rsp),%r9 /* Arg 6 */
-#undef SHADOW_BYTES
-UNLIKELY_END(trace)
- leaq hypercall_table(%rip),%r10
- PERFC_INCR(hypercalls, %rax, %rbx)
- callq *(%r10,%rax,8)
-#ifndef NDEBUG
- /* Deliberately corrupt parameter regs used by this hypercall. */
- popq %r10 # Shadow RIP
- cmpq %r10,UREGS_rip+8(%rsp)
- popq %rcx # Shadow hypercall index
- jne skip_clobber /* If RIP has changed then don't clobber. */
- leaq hypercall_args_table(%rip),%r10
- movb (%r10,%rcx,1),%cl
- movl $0xDEADBEEF,%r10d
- cmpb $1,%cl; jb skip_clobber; movq %r10,UREGS_rdi(%rsp)
- cmpb $2,%cl; jb skip_clobber; movq %r10,UREGS_rsi(%rsp)
- cmpb $3,%cl; jb skip_clobber; movq %r10,UREGS_rdx(%rsp)
- cmpb $4,%cl; jb skip_clobber; movq %r10,UREGS_r10(%rsp)
- cmpb $5,%cl; jb skip_clobber; movq %r10,UREGS_r8(%rsp)
- cmpb $6,%cl; jb skip_clobber; movq %r10,UREGS_r9(%rsp)
-skip_clobber:
-#endif
- movq %rax,UREGS_rax(%rsp) # save the return value
+ mov %rsp, %rdi
+ call pv_hypercall
/* %rbx: struct vcpu */
test_all_events:
call create_bounce_frame
jmp test_all_events
-bad_hypercall:
- movq $-ENOSYS,UREGS_rax(%rsp)
- jmp test_all_events
-
ENTRY(sysenter_entry)
sti
pushq $FLAT_USER_SS